use core::{ops::Deref, slice};

use memory_info::OTA_FLASH_ORIGIN;
use periph_utils::flash;
use utils::{bincodec::Decode, MutCell};

use crate::{app::error::{AppErr, BaseErr}, ota_info::OtaCtx};

struct Info {
    pkg_size: usize,
    pkg_id: u16,
    pkg_index: usize,
}

impl Info {

    fn init(&mut self, pkg_size: u32) {
        self.pkg_size = pkg_size as usize;
        self.pkg_id = 0;
        self.pkg_index = 0;
    }
}

static INFO: MutCell<Info> = MutCell::const_new(Info { 
    pkg_size: 0, 
    pkg_id: 0, 
    pkg_index: 0 
});

pub fn on_start(pkg_size: u32) -> Result<(), AppErr> {

    flash::erase(OTA_FLASH_ORIGIN as u32, pkg_size as usize);
    let info = unsafe { INFO.as_mut() };
    info.init(pkg_size);
    Ok(())
}

#[derive(Decode)]
pub struct Package<'a> {
    id: u16,
    buf: &'a [u8],
}

pub fn on_translate<'a>(pkg: Package<'a>) -> Result<(), AppErr> {
    
    let info = unsafe { INFO.as_mut() };
    if pkg.id != info.pkg_id {
        return Err(AppErr::Base(BaseErr::OtaIdInvalid));
    }

    flash::write((OTA_FLASH_ORIGIN + info.pkg_index) as u32, pkg.buf.as_ptr(), pkg.buf.len());
    
    info.pkg_index += pkg.buf.len();
    info.pkg_id += 1;
    Ok(())
}

pub fn on_complete(md5_data: &[u8]) -> Result<(), AppErr> {
    let info = unsafe { INFO.as_mut() };
    if info.pkg_size != info.pkg_index {
        return Err(AppErr::Base(BaseErr::OtaPkgInvalid));
    }
    let ota_data = unsafe { slice::from_raw_parts(
        OTA_FLASH_ORIGIN as *const u8,
        info.pkg_size 
    ) };
    let digest = md5::compute(ota_data);

    if md5_data != digest.deref() {
        return Err(AppErr::Base(BaseErr::OtaMd5Invalid));
    }

    OtaCtx.init(info.pkg_size);

    Ok(())
}









